/**
  * @file 		queue.c
  * @brief		包含队列相关逻辑以及功能实现的文件。
  *
  * @copyright  Copyright (c) 2020~2030 ShenZhen Dxtc Technology Co., Ltd. 
  * All rights reserved.
  *
  * @version	v3.1
  * @author		FengLi
  * @date 		2021-08-02
  *
  * @note		鼎新同创·智能锁
  * 
  * @par 修改日志:
  * <1> 2021/08/02 v3.1 FengLi 创建初始版本
  *******************************************************************/
#include "osal.h"
#include "device.h"

/** @brief   队列节点数据结构 */
typedef struct queue
{
    struct queue *next;  ///< 指针域
    uint32_t size;       ///< 数据域大小
    uint8_t  data[];     ///< 数据域
}QueueType_stu_t;

/** @brief   队列头节点数据结构 */
typedef struct
{
    uint16_t  max_len;   ///< 队列最大节点数（创建队列时设定）
    uint16_t  cur_len;   ///< 队列当前有效节点数
    struct queue *tail;  ///< 指向队列尾节点
}QueueInfo_stu_t;

/**
  * @brief  创建队列
  * @details 详细说明：无
  * 
  * @param[in]  len：创建的队列最大节点数
  *         
  * @return 返回（创建）队列数据结构指针
  */
SuperQueueHandle_t OSAL_QueueCreate(uint8_t len)
{
    QueueType_stu_t *pQueueHead;

    pQueueHead = (QueueType_stu_t *)OSAL_Malloc(sizeof(QueueType_stu_t) + sizeof(QueueInfo_stu_t));
    if (pQueueHead != NULL)
    {
        QueueInfo_stu_t *pQueueInfo = (QueueInfo_stu_t *)pQueueHead->data;

        pQueueHead->size = sizeof(QueueInfo_stu_t);
        pQueueHead->next = NULL;

        pQueueInfo->tail = NULL;
        pQueueInfo->cur_len = 0;
        pQueueInfo->max_len = len;
    }
    return pQueueHead;
}

/**
  * @brief  写队列
  * @details 详细说明：往一个队列里面插入一个新的消息
  * 
  * @param[in]  handle：队列句柄
  * @param[in]  msg：消息首地址
  * @param[in]  size：消息大小
  * @param[in]  insert：【SET】插队写入到队列最前面、【RESET】正常写入到队列的末尾
  *         
  * @return 【SUCCEE】：成功   【ERROR】：失败（错误)
  */
ErrorStatus OSAL_QueueSend(SuperQueueHandle_t handle, void *msg, uint16_t size, FlagStatus insert)
{
    QueueType_stu_t *pQueueHead, *pQueueNew;
    QueueInfo_stu_t *pQueueInfo;

    pQueueHead = (QueueType_stu_t *)handle;
    if (pQueueHead == NULL || msg == NULL)
    {
        return ERROR; //< 参数错误
    }

    Device_EnterCritical();
    pQueueInfo = (QueueInfo_stu_t *)pQueueHead->data;
    if (pQueueInfo->cur_len >= pQueueInfo->max_len)
    {
        Device_ExitCritical();
        return ERROR; //< 队列满了
    }

    pQueueNew = (QueueType_stu_t *)OSAL_Malloc(sizeof(QueueType_stu_t) + size);
    if (pQueueNew == NULL)
    {
        Device_ExitCritical();
        return ERROR; //< 内存不足
    }

    memcpy(pQueueNew->data, msg, size);
    pQueueNew->size = size;
    pQueueNew->next = NULL;

    if (pQueueHead->next == NULL)
    {
        /* 队列当前为空 */
        pQueueHead->next = pQueueNew;
        pQueueInfo->tail = pQueueNew;
    }
    else if (insert == SET)
    {
        /* 插队写（写到头节点后面）*/
        pQueueNew->next = pQueueHead->next;
        pQueueHead->next = pQueueNew;
    }
    else
    {
        /* 正常写（写到尾节点后面）*/
        pQueueInfo->tail->next = pQueueNew;
        pQueueInfo->tail = pQueueNew;
    }
    pQueueInfo->cur_len++;
    Device_ExitCritical();
    return SUCCESS;
}

/**
  * @brief   从队列里面接收一个消息（并删除该消息对应节点）
  * @details 详细说明：无
  * 
  * @param[in]  handle：队列句柄
  * @param[out] msg：用于存储队列中的消息
  *         
  * @return 返回接收的数据大小
  */
uint16_t OSAL_QueueReceive(SuperQueueHandle_t handle, void *msg)
{
    QueueType_stu_t *pQueueHead, *pQueue;
    QueueInfo_stu_t *pQueueInfo;
    uint16_t data_size;

    pQueueHead = (QueueType_stu_t *)handle;
    if (pQueueHead == NULL || msg == NULL)
    {
        return 0; //< 参数错误
    }

    Device_EnterCritical();
    pQueueInfo = (QueueInfo_stu_t *)pQueueHead->data;
    if (pQueueInfo->cur_len == 0 || pQueueHead->next == NULL)
    {
        Device_ExitCritical();
        return 0; //< 队列为空
    }
    pQueueInfo->cur_len--;

    pQueue = pQueueHead->next;
    memcpy(msg, pQueue->data, pQueue->size);
    data_size = pQueue->size;
    pQueueHead->next = pQueue->next;
    OSAL_Free(pQueue);
    Device_ExitCritical();
    return data_size;
}

/**
  * @brief   获取队列有效长度
  * @details 详细说明：无
  * 
  * @param[in]  handle：队列句柄
  * @param[in]  size：返回队列里面最前面那个数据的SIZE
  *         
  * @return 返回队列当前有效节点数
  */
uint16_t OSAL_QueueLenght(SuperQueueHandle_t handle, uint16_t *size)
{
    QueueType_stu_t *pQueueHead;
    QueueInfo_stu_t *pQueueInfo;
    uint16_t queue_len = 0;

    pQueueHead = (QueueType_stu_t *)handle;
    if (pQueueHead == NULL)
    {
        return 0; //< 参数错误
    }

    Device_EnterCritical();
    pQueueInfo = (QueueInfo_stu_t *)pQueueHead->data;
    if ((pQueueInfo->cur_len) > 0 && (size != NULL) && (pQueueHead->next != NULL))
    {
        *size = pQueueHead->next->size;
    }
    queue_len = pQueueInfo->cur_len;
    Device_ExitCritical();
    return queue_len;
}

/**
  * @brief   清空队列
  * @details 详细说明：无
  * 
  * @param[in]  handle：队列句柄
  *         
  * @return 【SUCCEE】：成功   【ERROR】：失败（错误)
  */
ErrorStatus OSAL_QueueReset(SuperQueueHandle_t handle)
{
    QueueType_stu_t *pQueueHead;
    QueueInfo_stu_t *pQueueInfo;

    pQueueHead = (QueueType_stu_t *)handle;
    if (pQueueHead == NULL)
    {
        return ERROR; //< 参数错误
    }
    
    Device_EnterCritical();
    while (pQueueHead->next != NULL)
    {
        QueueType_stu_t *pQueue = pQueueHead->next;
        pQueueHead->next = pQueue->next;
        OSAL_Free(pQueue);
    }
    pQueueInfo = (QueueInfo_stu_t *)pQueueHead->data;
    pQueueInfo->cur_len = 0;
    pQueueInfo->tail = NULL;
    Device_ExitCritical();
    return SUCCESS;
}

/**
  * @brief   遍历队列
  * @details 详细说明：无
  * 
  * @param[in]  handle：队列句柄
  * @param[in]  cb：队列遍历回调函数
  * @param[in]  param：遍历队列时传入的参数（执行回到函数时会将该参数传进去），为申请“OsPort_stu_t”结构空间地址
  *         
  * @return 0：表示删掉当前节点，  1：表示继续遍历，  -1：表示结束遍历
  */
uint16_t OSAL_QueueTraverse(SuperQueueHandle_t handle, QueueCb_fun_t cb, uint32_t param)
{
    QueueType_stu_t *pQueueHead;
    QueueInfo_stu_t *pQueueInfo;

    pQueueHead = (QueueType_stu_t *)handle;
    if (pQueueHead == NULL || cb == NULL)
    {
        return 0; //< 参数错误
    }

    pQueueInfo = (QueueInfo_stu_t *)pQueueHead->data;
    if (pQueueInfo->cur_len == 0)
    {
        return 0; //< 队列长度为0
    }

    QueueType_stu_t *pPre = pQueueHead;
    QueueType_stu_t *pCur = pQueueHead->next;

    for (; pCur != NULL; pCur = pCur->next)
    {
        int ret = cb(param, pCur->data, pCur->size);
        if (ret == 0)
        {
            pPre->next = pCur->next;
            if (pQueueInfo->tail == pCur)
            {
                pQueueInfo->tail = pPre;
            }
            OSAL_Free(pCur);
            pCur = pPre;
            pQueueInfo->cur_len--;
        }
        else if (ret < 0)
        {
            break;
        }
        else
        {
            pPre = pCur;
        }
    }
    return pQueueInfo->cur_len;
}
